import { SimpleFunction } from "src/shims";

type ListenName = string | symbol;
type Listener = SimpleFunction & { onceFn?: any };
export type Events = ReturnType<typeof createEvents>;

export function createEvents() {
  
  const getSubscribeMap = (() => {
    let events: Map<ListenName, Listener[]>;
    return () => {
      if (!events) {
        events = new Map();
      }
      return events;
    };
  })();

  const event = {
    emit: function(name: ListenName, ...args: any[]) {
      const subscribe = getSubscribeMap().get(name);
      if (!subscribe || !subscribe.length) return;
      subscribe.forEach(fn => fn.apply(this, args));
    },
    on: function(name: ListenName, fn: Listener) {
      const subscribeMap = getSubscribeMap();
      const subscribe = subscribeMap.get(name);
      if (!subscribe) {
        subscribeMap.set(name, [fn]);
      } else {
        subscribe.push(fn);
      }
      return () => event.off(name, fn);
    },
    once: function(name: ListenName, fn: Listener) {
      const onceFn = (...args: any[]) => {
        this.off(name, onceFn);
        fn.apply(this, args);
      };
      fn.onceFn = onceFn;
      this.on(name, onceFn);
      return () => event.off(name, fn);
    },
    off: function(name: ListenName, fn?: Listener) {
      let subscribeMap = getSubscribeMap();
      let subscribe = subscribeMap.get(name);
      if (!subscribe) return;
      if (!fn) {
        subscribeMap.set(name, []);
        return;
      }
      subscribeMap.set(
        name,
        subscribe.filter(item => item !== fn)
      );
    }
  };
  return event;
}
